package deepbrain.simnetwork.mechanism;

import java.util.HashMap;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Set;

import deepbrain.simnetwork.network.Network;
import deepbrain.simnetwork.network.NetworkState;
import deepbrain.simnetwork.network.NodeState;
import deepbrain.simnetwork.structure.Connection;

/**
 * The Bayes Mechanism represents the mechanism used in Bayes Inference Network
 * in which each node's state is determined by its Probability Table.
 * 
 * @author Li Yang
 * @create 2009-9-18
 */
public class BayesianMechanism extends GenericMechanism {

	private String variableName;
	private HashMap<BayesianJointVariableState, Double> probabilityTable = new HashMap<BayesianJointVariableState, Double>();

	@Override
	public void initialize(MechanismProperties properties) {
		super.initialize(properties);

		this.variableName = properties.getProperty("VariableName");
		String[] dependencies = properties.getProperty("Dependency").split(",");

		String _probabilityTable = properties.getProperty("ProbabilityTable");
		String[] probabilities = _probabilityTable.split(";");
		for (String probability : probabilities) {
			String[] splits = probability.split(":");
			double prob = Double.parseDouble(splits[1]);
			String[] splits2 = splits[0].substring(1, splits[0].length() - 1)
					.split(",");
			BayesianJointVariableState jointVariableState = new BayesianJointVariableState();
			if (!(dependencies.length == 1 && dependencies[0].trim().length() == 0))
				for (int i = 0; i < dependencies.length; i++) {
					jointVariableState.addVariableState(dependencies[i],
							splits2[i].equals("+") ? true : false);
				}
			probabilityTable.put(jointVariableState, prob);
		}

		// System.out.println(probabilityTable);
	}

	public NodeState nextNodeState(int node, Network network, NetworkState state) {

		Set<Connection<?>> afferentConnections = network
				.getAfferentConnections(node);

		Queue<BayesianJointProbability> list = new LinkedList<BayesianJointProbability>();
		list.add(new BayesianJointProbability());

		for (Connection<?> connection : afferentConnections) {
			int src = connection.source;
			BayesianMechanism mechanism = (BayesianMechanism) network
					.getMechanism(src);
			int size = list.size();
			for (int i = 0; i < size; i++) {
				BayesianJointProbability prob = list.remove();

				BayesianJointProbability positive = new BayesianJointProbability();
				positive.jointVariableState = prob.jointVariableState.clone()
						.addVariableState(mechanism.getVariableName(), true);
				positive.probability = prob.probability
						* state.getNodeState(src).getFiringProbability();
				list.add(positive);

				BayesianJointProbability negative = new BayesianJointProbability();
				negative.jointVariableState = prob.jointVariableState.clone()
						.addVariableState(mechanism.getVariableName(), false);
				negative.probability = prob.probability
						* state.getNodeState(src).getSilentProbability();

				list.add(negative);
			}
		}

		double prob = 0;

//		System.out.println(list);

		for (BayesianJointProbability jointProb : list) {
//			System.out.println(probabilityTable + "  "
//					+ jointProb.jointVariableState);
			prob += jointProb.probability
					* probabilityTable.get(jointProb.jointVariableState);
		}

		return NodeState.newNodeState(prob);
	}

	public String getVariableName() {
		return variableName;
	}

}